#include "PageCache.h"

PageCache PageCache::_Inst; // 静态成员初始化

void PageCache::AdjustMapAndSpan(Span* span, size_t k)
{
	if (k >= NPAGES) // 129 +
	{
		span->_isUse = true;
		span->_blocksize = k << PAGE_SHIFT;
		span->_n = k;
		span->_usecount = 1;
		//_IdSpanMap[span->_pageid] = span; // 更新map
		_IdSpanMap.set(span->_pageid, span);
	}
	else if (k > MAX_BYTES >> PAGE_SHIFT) // 32 ~ 128 
	{
		span->_isUse = true;
		span->_blocksize = k << PAGE_SHIFT;
		span->_n = k;
		span->_usecount = 1;
		//_IdSpanMap[span->_pageid] = span; // 更新map
		//_IdSpanMap[span->_pageid + span->_n - 1] = span; 
		_IdSpanMap.set(span->_pageid, span);
		_IdSpanMap.set(span->_pageid + span->_n - 1, span);
	}
	else // 1 ~ 32
	{
		// 映射关系的维护 分配给central的span需要保存每一个ID和span的映射关系
		for (PAGE_ID i = 0; i < span->_n; ++i)
		{
			//_IdSpanMap[span->_pageid + i] = span;
			_IdSpanMap.set(span->_pageid + i, span);
		}
		span->_isUse = true;
	}
}

// 从page池中拿k个span，如果没有就会向系统堆申请 // 调用前需要加锁
Span* PageCache::GetSpan(size_t k)
{
	assert(k);
	// kpage 大于 MAX_BYTES >> PAGE_SHIFT
	if (k >= NPAGES) // 129+
	{
		//Span* span = new Span;
		Span* span = _span_pool.New();
		void* ptr = SystemAlloc(k);
		span->_pageid = (PAGE_ID)ptr >> PAGE_SHIFT;
		AdjustMapAndSpan(span, k);
		return span;
	}

	// 下面CentralCache申请 1 ~ 128

	// 有对应的page 
	if (!_pagelist[k].Empty())
	{
		Span* retspan = _pagelist[k].PopFront();
		// 映射关系的维护 分配给central的span需要保存每一个ID和span的映射关系
		AdjustMapAndSpan(retspan, k);
		/*for (PAGE_ID i = 0; i < retspan->_n; ++i)
		{
			_IdSpanMap[retspan->_pageid + i] = retspan;
		}
		retspan->_isUse = true;*/
		return retspan;
	}

	// 没有对应的page，从下面找
	for (size_t i = k + 1; i < NPAGES; ++i)
	{
		if (!_pagelist[i].Empty())
		{
			Span* ispan = _pagelist[i].PopFront();
			//Span* newspan = new Span;
			Span* newspan = _span_pool.New();

			newspan->_pageid = ispan->_pageid; // 页id
			ispan->_pageid += k;
			newspan->_n = k;
			ispan->_n -= k;
			
			_pagelist[ispan->_n].PushFront(ispan);

			// 映射关系的维护
			// 位于page池中的span，未分配给central的span只需要保存起始和结束的id
			//_IdSpanMap[ispan->_pageid] = ispan; 
			//_IdSpanMap[ispan->_pageid + ispan->_n - 1] = ispan;
			 _IdSpanMap.set(ispan->_pageid, ispan);
			 _IdSpanMap.set(ispan->_pageid + ispan->_n - 1, ispan);
			// 分配给central的span需要保存每一个ID和span的映射关系
			//AdjustMapAndSpan(newspan, k);
			for (PAGE_ID i = 0; i < newspan->_n; ++i)
			{
				//_IdSpanMap[newspan->_pageid + i] = newspan;
			    _IdSpanMap.set(newspan->_pageid + i, newspan);
			}
			newspan->_isUse = true;
			return newspan;
		}
	}

	// 整个pagelist没有可以给的空间了，从系统拿
	void* newalloc = SystemAlloc(NPAGES - 1);
	//Span* newspan = new Span;
	Span* newspan = _span_pool.New();
	newspan->_n = NPAGES - 1;
	newspan->_pageid = (PAGE_ID)newalloc >> PAGE_SHIFT;
	_pagelist[newspan->_n].PushFront(newspan);

	// cout << "newallocip: " << newalloc << "_pageid: " << newspan->_pageid << endl; ///////////////
	return GetSpan(k);
}

// block的ID和span的对应关系，通过block地址拿到对应的span指针
Span* PageCache::MapBlockToSpan(void* block)
{
	//std::lock_guard<std::mutex> lk(_page_mtx); // 加锁 // 无需加锁
	PAGE_ID pageid = (PAGE_ID)block >> PAGE_SHIFT;
	//auto it = _IdSpanMap.find(pageid);
	Span* it = (Span*)_IdSpanMap.get(pageid);
	if (it != nullptr)
	{
		return it;
	}
	else
	{
		assert(false);
		return nullptr;
	}
}

// 在释放span到page池之前需要加page锁
void PageCache::ReleaseSpanToPageCache(Span* span)
{
	assert(span);
	assert(span->_isUse == false && span->_usecount == 0);

	if (span->_blocksize == span->_n << PAGE_SHIFT) // 表示是size大于MAX_BYTES
	{
		if (span->_n < NPAGES) // 可以通过page释放
		{ // 和下面的逻辑一样
		}
		else // sysfree
		{
			SystemFree((void*)(span->_pageid << PAGE_SHIFT), span->_blocksize);
			return;
		}
	}
	else // CentralCache归还的span
	{
		// 需要对map的映射关系做一下更新
		for (PAGE_ID i = 1; i < span->_n - 1; ++i) // 把开头结尾中间的页ID映射去掉
		{
			//size_t num = _IdSpanMap.erase(span->_pageid + i);
			//assert(num); // num != 0

			_IdSpanMap.set(span->_pageid + i, nullptr);
		}
	}

	span->_blocksize = 0;
	span->_list = nullptr;

	// 向前找可以合并的页
	while (true) 
	{
		//auto it = _IdSpanMap.find(span->_pageid - 1);
		Span* it = (Span*)_IdSpanMap.get(span->_pageid - 1);
		// 前一个span不存在
		if (it == nullptr)
			break;
		// 前一个span正在被使用
		Span* prevspan = it;

		if (prevspan->_isUse == true)
			break;
		// 前一个span和并后，大于NPAGES无法管理
		if (prevspan->_n + span->_n > NPAGES - 1)
			break;
		// 可以和prevspan合并
		//_IdSpanMap.erase(span->_pageid); // 删除中间两个
		//_IdSpanMap.erase(it);
		_IdSpanMap.set(span->_pageid, nullptr); // 删除中间两个
		_IdSpanMap.set(it->_pageid, nullptr);
		span->_pageid = prevspan->_pageid;
		span->_n += prevspan->_n;

		//_IdSpanMap[span->_pageid] = span; // 修改边界
		//_IdSpanMap[span->_pageid + span->_n - 1] = span;
		_IdSpanMap.set(span->_pageid, span); // 修改边界
		_IdSpanMap.set(span->_pageid + span->_n - 1, span);

		_pagelist[prevspan->_n].Ereas(prevspan);
		//delete prevspan;
		//cout << "向前合并成功 newkpage: " << span->_n << endl;
		_span_pool.Delete(prevspan);
	}

	// 向后找可以合并的页
	while (true)
	{
		Span* it = (Span*)_IdSpanMap.get(span->_pageid + span->_n);
		// 后一个span不存在
		if (it == nullptr)
			break;
		// 后一个span正在被使用
		Span* nextspan = it;

		if (nextspan->_isUse == true)
			break;
		// 后一个span和并后，大于NPAGES无法管理
		if (nextspan->_n + span->_n > NPAGES - 1)
			break;
		// 可以和nextspan合并
		//_IdSpanMap.erase(it); // 删除中间两个
		//_IdSpanMap.erase(span->_pageid + span->_n - 1);
		_IdSpanMap.set(it->_pageid, nullptr); // 删除中间两个
		_IdSpanMap.set(span->_pageid + span->_n - 1, nullptr);

		span->_n += nextspan->_n;
		_IdSpanMap.set(span->_pageid + span->_n - 1, span); // 修改边界
		_IdSpanMap.set(span->_pageid, span);

		_pagelist[nextspan->_n].Ereas(nextspan);
		//delete nextspan;
		//cout << "向后合并成功 newkpage: " << span->_n << endl;
		_span_pool.Delete(nextspan);
	}

	// span 需要放到合适的位置 pagelist需要更改
	_pagelist[span->_n].PushFront(span);

}
